#include <common.h>
#include <heap.h>
#include <vmm.h>

unsigned int spinlock = 0;

unsigned int heap_max = HEAP_START;
header *heap_first = 0;

void init_heap()
{
	
}

unsigned int alloc_chunk(unsigned int start, unsigned int length)
{
	unsigned int l = start-heap_max;
	while(start + length > heap_max)
	{
		unsigned int page = pmm_alloc_page();
		map(heap_max, page, PAGE_PRESENT | PAGE_WRITE | PAGE_USER);
		heap_max += 0x1000;
		l += 0x1000;
	}
	return (l);
}

void free_chunk(header *chunk)
{
	if(chunk->prev)
		chunk->prev->next = 0;
		
	if(chunk->prev == 0)
		heap_first = 0;


	while ((heap_max-0x1000) >= (unsigned int)chunk)
	{
		heap_max -= 0x1000;
		unsigned int page;
		get_mapping(heap_max, &page);
		pmm_free_page(page);
		unmap(heap_max);
	}
}

void split_chunk(header *chunk, unsigned int length)
{
	if(chunk->magic != HEAP_MAGIC)
		panic("MEMORY ERROR split_chunk");
	if(chunk->length - length > sizeof(header))
	{
	
		header *newchunk = (header *)((unsigned int)chunk + (length));
		newchunk->prev = chunk;
		newchunk->next = chunk->next;
		newchunk->allocated = 0;
		newchunk->magic = HEAP_MAGIC;
		newchunk->length = chunk->length - length;
		
		chunk->next = newchunk;
		chunk->length = length;
	}
}

void glue_chunk(header *chunk)
{
	if(chunk->magic != HEAP_MAGIC)
		panic("MEMORY ERROR glue_chunk");
	if (chunk->next) 
	{
		if(chunk->next->magic != HEAP_MAGIC)
			panic("MEMORY ERROR glue_chunk next");
	if(chunk->next->allocated == 0)
	{
	//	printf("joinn(%x-%x)",chunk->next,(unsigned int)chunk->next + chunk->next->length);
		chunk->length = chunk->length + chunk->next->length;
		if(chunk->next->next != 0)
		{
			chunk->next->next->prev = chunk;
		}
		chunk->next = chunk->next->next;
	}
	}
	
	if(chunk->prev)
	{
		if(chunk->prev->magic != HEAP_MAGIC)
			panic("MEMORY ERROR glue_chunk prev");
	if(chunk->prev->allocated == 0)
	{
		//printf("joinp(%x-%x)",chunk->prev,(unsigned int)chunk->prev + chunk->prev->length);
		chunk->prev->length = chunk->prev->length + chunk->length;
		chunk->prev->next = chunk->next;
		if(chunk->next)
		{
			chunk->next->prev = chunk->prev;
		}
		chunk = chunk->prev;
	}
	}
	
	if(chunk->next == 0)
	{
		//printf("Free");
		free_chunk(chunk);
	}
}

void *kmalloc(unsigned int l)
{
	spin(&spinlock);
	l += sizeof(header);
	
	header *cur_header = heap_first, *prev_header = 0;
	while(cur_header)
	{
		if(cur_header->magic != HEAP_MAGIC)
		{
			printf("cur_header:%x",prev_header);
			for(;;);
			panic("MEMORY ERROR kmalloc cur_header");
		}
		if((cur_header->allocated == 0) && (cur_header->length >= l))
		{
			split_chunk(cur_header, l);
			cur_header->allocated = 1;
			//printf("\nkmalloc(%x-",cur_header);
			//printf("%x)", (unsigned int)cur_header + cur_header->length);
			//printf("n %x", cur_header->next);
			return (void *)((unsigned int)cur_header + sizeof(header));
		}
		prev_header = cur_header;
		cur_header = cur_header->next;
	}
	
	unsigned int chunk_start;
	if(prev_header)
	{
		chunk_start = (unsigned int)prev_header + prev_header->length;
	} else {
		chunk_start = HEAP_START;
		heap_first = (header *)chunk_start;
	}

	alloc_chunk(chunk_start, l);

	cur_header = (header *)chunk_start;
	cur_header->prev = prev_header;

	cur_header->next = 0;
	cur_header->allocated = 1;
	cur_header->length = l;
	cur_header->magic = HEAP_MAGIC;
	if(prev_header)
		prev_header->next = cur_header;
	spinlock = 0;
	//printf("\nkmalloc(%x-",cur_header);
	//printf("%x)--", (unsigned int)cur_header + cur_header->length);
	return (void *)((unsigned int)cur_header + sizeof(header));
}

void kfree(void *p)
{

	spin(&spinlock);
	spinlock = 1;
	header *head = (header *)((unsigned int)p - sizeof(header));
	if(head->magic != HEAP_MAGIC)
	{
		printf("\n error in free(%x)",head);
		for(;;);
		panic("MEMORY ERROR kfree");
	}
	head->allocated = 0;
	//printf("\n  kfree(%x-", head);
	//printf("%x)", (unsigned int)head + head->length);
	//printf("n %x",head->next);
	glue_chunk(head);
	spinlock = 0;
}
